﻿using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using System;

namespace AutumnEngine
{
    /// <summary>
    /// A sprite node with a physical body that reacts to gravity and other physic nodes.
    /// </summary>
    public class PhysicsSpriteNode : SpriteNode
    {
        float _width, _height;
        public PhysicsSpriteNode(string texture, BodyType physType, float width, float height, float mass)
            : base(texture)
        {
            _width = width;
            _height = height;
            Width = (int)width;
            Height = (int)height;
            Mass = mass;
            Type = physType;           
        }
        bool createdBody = false;

        BodyType type;
        public BodyType Type
        {
            get { return type; }
            set
            {
                if (createdBody)
                {
                    Engine.Modules.PhysicsManager.sims[Engine.Modules.SceneManager.Scenes[this.InScene].ID].Remove(Body);
                    Engine.Modules.PhysicsManager.sims[Engine.Modules.SceneManager.Scenes[this.InScene].ID].Remove(Geom);
                }                
                type = value;
                
                createBody();
            }
        }

        void createBody()
        {
            bool stat = false;
            if (createdBody)
            {
                stat = this.Static;
            }
            switch (type)
            {
                case BodyType.Rectangular:
                    {
                        Vector2 oldPos = Vector2.Zero, linear = Vector2.Zero;
                        float oldRot = 0f;
                        if (createdBody)
                        {
                            oldPos = Position;
                            oldRot = Rotation;
                            linear = Body.LinearVelocity;
                        }
                        Body = BodyFactory.Instance.CreateRectangleBody(_width, _height, mass);
                        if (createdBody)
                        {
                            //Body already exists.
                            Body.Position = oldPos;
                            Body.Rotation = oldRot;
                            Body.LinearVelocity = linear;
                            Geom = GeomFactory.Instance.CreateRectangleGeom(Body, _width, _height);                            
                            Engine.Modules.PhysicsManager.AddNode(Engine.Modules.SceneManager.Scenes[this.InScene].ID, this);
                        }
                        else
                        {
                            Geom = GeomFactory.Instance.CreateRectangleGeom(Body, _width, _height);                            
                        }
                        break;
                    }

                case BodyType.Automatic:
                    {
                        Vector2 oldPos = Vector2.Zero, linear = Vector2.Zero;
                        float oldRot = 0f;
                        if (createdBody)
                        {
                            oldPos = Position;
                            oldRot = Rotation;
                            linear = Body.LinearVelocity;
                        }
                        uint[] data = new uint[this.Texture2D.Width * this.Texture2D.Height];
                        this.Texture2D.GetData(data);
                        Vertices verts = Vertices.CreatePolygon(data, this.Texture2D.Width, this.Texture2D.Height);
                        Vector2 orig = verts.GetCentroid();
                        Body = BodyFactory.Instance.CreatePolygonBody(verts, mass);
                        if (createdBody)
                        {
                            //Body already exists.
                            Body.Position = oldPos;
                            Body.Rotation = oldRot;
                            Body.LinearVelocity = linear;
                            Geom = GeomFactory.Instance.CreatePolygonGeom(Body, verts, 0);
                            Engine.Modules.PhysicsManager.AddNode(Engine.Modules.SceneManager.Scenes[this.InScene].ID, this);
                        }
                        else
                        {
                            Geom = GeomFactory.Instance.CreatePolygonGeom(Body, verts, 0);

                        }
                        break;
                    }
            }
            this.Static = stat;
            createdBody = true;
        }
        

        public int Width
        {
            get;
            set;
        }

        public int Height
        {
            get;
            set;
        }

        float mass;
        public float Mass
        {
            get { return mass; }
            set { mass = value; }
        }

        /// <summary>
        /// Gets or sets the body.
        /// </summary>
        /// <value>The body.</value>
        public Body Body
        {
            get;
            set;
        }   

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="PhysicsSpriteNode"/> is static.
        /// </summary>
        /// <value><c>true</c> if static; otherwise, <c>false</c>.</value>
        public bool Static
        {
            get { return Body.IsStatic; }
            set { Body.IsStatic = value; }
        }

        /// <summary>
        /// Gets or sets the position.
        /// </summary>
        /// <value>The position.</value>
        public override Vector2 Position
        {
            get { return Body.Position; }
            set { Body.Position = value; }
        }

        /// <summary>
        /// Gets or sets the origin.
        /// </summary>
        /// <value>The origin.</value>
        public override Vector2 Origin
        {
            get
            {
                return new Vector2(_width / 2, _height / 2);
            }
            set
            {
                base.Origin = value;
            }
        }

        /// <summary>
        /// Gets the rotation.
        /// </summary>
        /// <value>The rotation.</value>
        public override float Rotation
        {
            get { return Body.Rotation; }
            set { Body.Rotation = value; }
        }

        /// <summary>
        /// Gets or sets the scale.
        /// </summary>
        /// <value>The scale.</value>
        public override Vector2 Scale
        {
            get
            {
                //Cannot change scale.
                return new Vector2(1f);
            }
            set
            {
                base.Scale = value;
            }
        }

        /// <summary>
        /// Gets or sets the geom.
        /// </summary>
        /// <value>The geom.</value>
        public Geom Geom
        {
            get;
            set;
        }

        public void ApplyForce(Vector2 force)
        {
            Body.ApplyForce(ref force);
        }

        public void ApplyTorque(float torque)
        {
            Body.ApplyTorque(torque);
        }

        public void ApplyImpulseForce(Vector2 imp)
        {
            Body.ApplyImpulse(ref imp);
        }

        protected override void ChangedTexture()
        {
            bool stat = this.Static;
            if (createdBody)
            {
                Engine.Modules.PhysicsManager.sims[Engine.Modules.SceneManager.Scenes[this.InScene].ID].Remove(Body);
                Engine.Modules.PhysicsManager.sims[Engine.Modules.SceneManager.Scenes[this.InScene].ID].Remove(Geom);
            }   
            createBody();
            this.Static = stat;
            base.ChangedTexture();
        }

    }


    /// <summary>
    /// 
    /// </summary>
    public enum BodyType
    {
        /// <summary>
        /// A rectangular body.
        /// </summary>
        Rectangular = 0,
        Automatic = 1
    }
}
